/* XzDec.c -- Xz Decode
   2009-06-08 : Igor Pavlov : Public domain */

/* #define XZ_DUMP */

#include "Types.h"

#include "7zCrc.h"
#include "Alloc.h"
#include "Bra.h"
#include "CpuArch.h"
#include "Delta.h"
#include "Lzma2Dec.h"

#ifdef USE_SUBBLOCK
    #include "SbDec.h"
#endif

#include "Xz.h"

#define XZ_CHECK_SIZE_MAX 64

#define CODER_BUF_SIZE (1 << 17)

unsigned Xz_ReadVarInt(const Byte* p, size_t maxSize, UInt64* value)
{
    int i, limit;
    *value = 0;
    limit = (maxSize > 9) ? 9 : (int)maxSize;

    for (i = 0; i < limit;)
    {
        Byte b = p[i];
        *value |= (UInt64)(b & 0x7F) << (7 * i++);
        if ((b & 0x80) == 0)
            return (b == 0 && i != 1) ? 0 : i;
    }
    return 0;
}

/* ---------- BraState ---------- */

#define BRA_BUF_SIZE (1 << 14)

typedef struct
{
    size_t bufPos;
    size_t bufConv;
    size_t bufTotal;

    UInt32 methodId;
    int encodeMode;
    UInt32 delta;
    UInt32 ip;
    UInt32 x86State;
    Byte deltaState[DELTA_STATE_SIZE];

    Byte buf[BRA_BUF_SIZE];
} CBraState;

void BraState_Free(void* pp, ISzAlloc* alloc)
{
    alloc->Free(alloc, pp);
}

SRes BraState_SetProps(void* pp, const Byte* props, size_t propSize, ISzAlloc* alloc)
{
    CBraState* p = ((CBraState*)pp);
    alloc = alloc;
    p->encodeMode = 0;
    p->ip = 0;
    if (p->methodId == XZ_ID_Delta)
    {
        if (propSize != 1)
            return SZ_ERROR_UNSUPPORTED;
        p->delta = (unsigned)props[0] + 1;
    }
    else
    {
        if (propSize == 4)
        {
            UInt32 v = GetUi32(props);
            switch (p->methodId)
            {
                case XZ_ID_PPC:
                case XZ_ID_ARM:
                case XZ_ID_SPARC:
                    if ((v & 3) != 0)
                        return SZ_ERROR_UNSUPPORTED;
                    break;
                case XZ_ID_ARMT:
                    if ((v & 1) != 0)
                        return SZ_ERROR_UNSUPPORTED;
                    break;
                case XZ_ID_IA64:
                    if ((v & 0xF) != 0)
                        return SZ_ERROR_UNSUPPORTED;
                    break;
            }
            p->ip = v;
        }
        else if (propSize != 0)
            return SZ_ERROR_UNSUPPORTED;
    }
    return SZ_OK;
}

void BraState_Init(void* pp)
{
    CBraState* p = ((CBraState*)pp);
    p->bufPos = p->bufConv = p->bufTotal = 0;
    x86_Convert_Init(p->x86State);
    if (p->methodId == XZ_ID_Delta)
        Delta_Init(p->deltaState);
}

static SRes BraState_Code(void* pp, Byte* dest, SizeT* destLen, const Byte* src, SizeT* srcLen,
                          int srcWasFinished, ECoderFinishMode finishMode, int* wasFinished)
{
    CBraState* p = ((CBraState*)pp);
    SizeT destLenOrig = *destLen;
    SizeT srcLenOrig = *srcLen;
    *destLen = 0;
    *srcLen = 0;
    finishMode = finishMode;
    *wasFinished = 0;
    while (destLenOrig > 0)
    {
        if (p->bufPos != p->bufConv)
        {
            size_t curSize = p->bufConv - p->bufPos;
            if (curSize > destLenOrig)
                curSize = destLenOrig;
            memcpy(dest, p->buf + p->bufPos, curSize);
            p->bufPos += curSize;
            *destLen += curSize;
            dest += curSize;
            destLenOrig -= curSize;
            continue;
        }
        p->bufTotal -= p->bufPos;
        memmove(p->buf, p->buf + p->bufPos, p->bufTotal);
        p->bufPos = 0;
        p->bufConv = 0;
        {
            size_t curSize = BRA_BUF_SIZE - p->bufTotal;
            if (curSize > srcLenOrig)
                curSize = srcLenOrig;
            memcpy(p->buf + p->bufTotal, src, curSize);
            *srcLen += curSize;
            src += curSize;
            srcLenOrig -= curSize;
            p->bufTotal += curSize;
        }
        if (p->bufTotal == 0)
            break;
        switch (p->methodId)
        {
            case XZ_ID_Delta:
                if (p->encodeMode)
                    Delta_Encode(p->deltaState, p->delta, p->buf, p->bufTotal);
                else
                    Delta_Decode(p->deltaState, p->delta, p->buf, p->bufTotal);
                p->bufConv = p->bufTotal;
                break;
            case XZ_ID_X86:
                p->bufConv = x86_Convert(p->buf, p->bufTotal, p->ip, &p->x86State, p->encodeMode);
                break;
            default:
                return SZ_ERROR_UNSUPPORTED;
        }
        p->ip += (UInt32)p->bufConv;

        if (p->bufConv == 0)
        {
            if (!srcWasFinished)
                break;
            p->bufConv = p->bufTotal;
        }
    }
    if (p->bufTotal == p->bufPos && srcLenOrig == 0 && srcWasFinished)
        *wasFinished = 1;
    return SZ_OK;
}

SRes BraState_SetFromMethod(IStateCoder* p, UInt64 id, ISzAlloc* alloc)
{
    CBraState* decoder;
    if (id != XZ_ID_Delta &&
        id != XZ_ID_X86 &&
        id != XZ_ID_PPC &&
        id != XZ_ID_IA64 &&
        id != XZ_ID_ARM &&
        id != XZ_ID_ARMT &&
        id != XZ_ID_SPARC)
        return SZ_ERROR_UNSUPPORTED;
    p->p = 0;
    decoder = alloc->Alloc(alloc, sizeof(CBraState));
    if (decoder == 0)
        return SZ_ERROR_MEM;
    decoder->methodId = (UInt32)id;
    p->p = decoder;
    p->Free = BraState_Free;
    p->SetProps = BraState_SetProps;
    p->Init = BraState_Init;
    p->Code = BraState_Code;
    return SZ_OK;
}

/* ---------- SbState ---------- */

#ifdef USE_SUBBLOCK

static void SbState_Free(void* pp, ISzAlloc* alloc)
{
    CSubblockDec* p = (CSubblockDec*)pp;
    SubblockDec_Free(p, alloc);
    alloc->Free(alloc, pp);
}

static SRes SbState_SetProps(void* pp, const Byte* props, size_t propSize, ISzAlloc* alloc)
{
    pp = pp;
    props = props;
    alloc = alloc;
    return (propSize == 0) ? SZ_OK : SZ_ERROR_UNSUPPORTED;
}

static void SbState_Init(void* pp)
{
    SubblockDec_Init((CSubblockDec*)pp);
}

static SRes SbState_Code(void* pp, Byte* dest, SizeT* destLen, const Byte* src, SizeT* srcLen,
                         int srcWasFinished, ECoderFinishMode finishMode, int* wasFinished)
{
    ECoderStatus status;
    SRes res = SubblockDec_Decode((CSubblockDec*)pp, dest, destLen, src, srcLen, finishMode, &status);
    srcWasFinished = srcWasFinished;
    *wasFinished = (status == LZMA_STATUS_FINISHED_WITH_MARK);
    return res;
}

SRes SbState_SetFromMethod(IStateCoder* p, ISzAlloc* alloc)
{
    CSubblockDec* decoder;
    p->p = 0;
    decoder = alloc->Alloc(alloc, sizeof(CSubblockDec));
    if (decoder == 0)
        return SZ_ERROR_MEM;
    p->p = decoder;
    p->Free = SbState_Free;
    p->SetProps = SbState_SetProps;
    p->Init = SbState_Init;
    p->Code = SbState_Code;
    SubblockDec_Construct(decoder);
    return SZ_OK;
}
#endif

/* ---------- Lzma2State ---------- */

static void Lzma2State_Free(void* pp, ISzAlloc* alloc)
{
    Lzma2Dec_Free((CLzma2Dec*)pp, alloc);
    alloc->Free(alloc, pp);
}

static SRes Lzma2State_SetProps(void* pp, const Byte* props, size_t propSize, ISzAlloc* alloc)
{
    if (propSize != 1)
        return SZ_ERROR_UNSUPPORTED;
    return Lzma2Dec_Allocate((CLzma2Dec*)pp, props[0], alloc);
}

static void Lzma2State_Init(void* pp)
{
    Lzma2Dec_Init((CLzma2Dec*)pp);
}

static SRes Lzma2State_Code(void* pp, Byte* dest, SizeT* destLen, const Byte* src, SizeT* srcLen,
                            int srcWasFinished, ECoderFinishMode finishMode, int* wasFinished)
{
    ELzmaStatus status;
    /* ELzmaFinishMode fm = (finishMode == LZMA_FINISH_ANY) ? LZMA_FINISH_ANY : LZMA_FINISH_END; */
    SRes res = Lzma2Dec_DecodeToBuf((CLzma2Dec*)pp, dest, destLen, src, srcLen, finishMode, &status);
    srcWasFinished = srcWasFinished;
    *wasFinished = (status == LZMA_STATUS_FINISHED_WITH_MARK);
    return res;
}

static SRes Lzma2State_SetFromMethod(IStateCoder* p, ISzAlloc* alloc)
{
    CLzma2Dec* decoder = alloc->Alloc(alloc, sizeof(CLzma2Dec));
    p->p = decoder;
    if (decoder == 0)
        return SZ_ERROR_MEM;
    p->Free = Lzma2State_Free;
    p->SetProps = Lzma2State_SetProps;
    p->Init = Lzma2State_Init;
    p->Code = Lzma2State_Code;
    Lzma2Dec_Construct(decoder);
    return SZ_OK;
}


void MixCoder_Construct(CMixCoder* p, ISzAlloc* alloc)
{
    int i;
    p->alloc = alloc;
    p->buf = 0;
    p->numCoders = 0;
    for (i = 0; i < MIXCODER_NUM_FILTERS_MAX; i++)
        p->coders[i].p = NULL;
}

void MixCoder_Free(CMixCoder* p)
{
    int i;
    for (i = 0; i < p->numCoders; i++)
    {
        IStateCoder* sc = &p->coders[i];
        if (p->alloc && sc->p)
            sc->Free(sc->p, p->alloc);
    }
    p->numCoders = 0;
    if (p->buf)
        p->alloc->Free(p->alloc, p->buf);
}

void MixCoder_Init(CMixCoder* p)
{
    int i;
    for (i = 0; i < p->numCoders - 1; i++)
    {
        p->size[i] = 0;
        p->pos[i] = 0;
        p->finished[i] = 0;
    }
    for (i = 0; i < p->numCoders; i++)
    {
        IStateCoder* coder = &p->coders[i];
        coder->Init(coder->p);
    }
}

SRes MixCoder_SetFromMethod(CMixCoder* p, int coderIndex, UInt64 methodId)
{
    IStateCoder* sc = &p->coders[coderIndex];
    p->ids[coderIndex] = methodId;
    switch (methodId)
    {
        case XZ_ID_LZMA2: return Lzma2State_SetFromMethod(sc, p->alloc);
            #ifdef USE_SUBBLOCK
        case XZ_ID_Subblock: return SbState_SetFromMethod(sc, p->alloc);
            #endif
    }
    if (coderIndex == 0)
        return SZ_ERROR_UNSUPPORTED;
    return BraState_SetFromMethod(sc, methodId, p->alloc);
}

SRes MixCoder_Code(CMixCoder* p, Byte* dest, SizeT* destLen,
                   const Byte* src, SizeT* srcLen, int srcWasFinished,
                   ECoderFinishMode finishMode, ECoderStatus* status)
{
    SizeT destLenOrig = *destLen;
    SizeT srcLenOrig = *srcLen;
    Bool allFinished = True;
    *destLen = 0;
    *srcLen = 0;
    *status = CODER_STATUS_NOT_FINISHED;

    if (p->buf == 0)
    {
        p->buf = p->alloc->Alloc(p->alloc, CODER_BUF_SIZE * (MIXCODER_NUM_FILTERS_MAX - 1));
        if (p->buf == 0)
            return SZ_ERROR_MEM;
    }

    if (p->numCoders != 1)
        finishMode = CODER_FINISH_ANY;

    for (;;)
    {
        Bool processed = False;
        int i;
        /*
           if (p->numCoders == 1 && *destLen == destLenOrig && finishMode == LZMA_FINISH_ANY)
           break;
         */

        for (i = 0; i < p->numCoders; i++)
        {
            SRes res;
            IStateCoder* coder = &p->coders[i];
            Byte* destCur;
            SizeT destLenCur, srcLenCur;
            const Byte* srcCur;
            int srcFinishedCur;
            int encodingWasFinished;

            if (i == 0)
            {
                srcCur = src;
                srcLenCur = srcLenOrig - *srcLen;
                srcFinishedCur = srcWasFinished;
            }
            else
            {
                srcCur = p->buf + (CODER_BUF_SIZE * (i - 1)) + p->pos[i - 1];
                srcLenCur = p->size[i - 1] - p->pos[i - 1];
                srcFinishedCur = p->finished[i - 1];
            }

            if (i == p->numCoders - 1)
            {
                destCur = dest;
                destLenCur = destLenOrig - *destLen;
            }
            else
            {
                if (p->pos[i] != p->size[i])
                    continue;
                destCur = p->buf + (CODER_BUF_SIZE * i);
                destLenCur = CODER_BUF_SIZE;
            }

            res = coder->Code(coder->p, destCur, &destLenCur, srcCur, &srcLenCur, srcFinishedCur, finishMode, &encodingWasFinished);

            if (!encodingWasFinished)
                allFinished = False;

            if (i == 0)
            {
                *srcLen += srcLenCur;
                src += srcLenCur;
            }
            else
            {
                p->pos[i - 1] += srcLenCur;
            }

            if (i == p->numCoders - 1)
            {
                *destLen += destLenCur;
                dest += destLenCur;
            }
            else
            {
                p->size[i] = destLenCur;
                p->pos[i] = 0;
                p->finished[i] = encodingWasFinished;
            }

            if (res != SZ_OK)
                return res;

            if (destLenCur != 0 || srcLenCur != 0)
                processed = True;
        }
        if (!processed)
            break;
    }
    if (allFinished)
        *status = CODER_STATUS_FINISHED_WITH_MARK;
    return SZ_OK;
}

SRes Xz_ParseHeader(CXzStreamFlags* p, const Byte* buf)
{
    *p = (CXzStreamFlags)GetBe16(buf + XZ_SIG_SIZE);
    if (CrcCalc(buf + XZ_SIG_SIZE, XZ_STREAM_FLAGS_SIZE) !=
        GetUi32(buf + XZ_SIG_SIZE + XZ_STREAM_FLAGS_SIZE))
        return SZ_ERROR_NO_ARCHIVE;
    return XzFlags_IsSupported(*p) ? SZ_OK : SZ_ERROR_UNSUPPORTED;
}

static Bool Xz_CheckFooter(CXzStreamFlags flags, UInt64 indexSize, const Byte* buf)
{
    return
        indexSize == (((UInt64)GetUi32(buf + 4) + 1) << 2) &&
        (GetUi32(buf) == CrcCalc(buf + 4, 6) &&
         flags == GetBe16(buf + 8) &&
         memcmp(buf + 10, XZ_FOOTER_SIG, XZ_FOOTER_SIG_SIZE) == 0);
}

#define READ_VARINT_AND_CHECK(buf, pos, size, res) \
    { unsigned s = Xz_ReadVarInt(buf + pos, size - pos, res); \
      if (s == 0) return SZ_ERROR_ARCHIVE;pos += s; }


SRes XzBlock_Parse(CXzBlock* p, const Byte* header)
{
    unsigned pos;
    int numFilters, i;
    UInt32 headerSize = (UInt32)header[0] << 2;

    if (CrcCalc(header, headerSize) != GetUi32(header + headerSize))
        return SZ_ERROR_ARCHIVE;

    pos = 1;
    if (pos == headerSize)
        return SZ_ERROR_ARCHIVE;
    p->flags = header[pos++];

    if (XzBlock_HasPackSize(p))
    {
        READ_VARINT_AND_CHECK(header, pos, headerSize, &p->packSize);
        if (p->packSize == 0 || p->packSize + headerSize >= (UInt64)1 << 63)
            return SZ_ERROR_ARCHIVE;
    }

    if (XzBlock_HasUnpackSize(p))
        READ_VARINT_AND_CHECK(header, pos, headerSize, &p->unpackSize);

    numFilters = XzBlock_GetNumFilters(p);
    for (i = 0; i < numFilters; i++)
    {
        CXzFilter* filter = p->filters + i;
        UInt64 size;
        READ_VARINT_AND_CHECK(header, pos, headerSize, &filter->id);
        READ_VARINT_AND_CHECK(header, pos, headerSize, &size);
        if (size > headerSize - pos || size > XZ_FILTER_PROPS_SIZE_MAX)
            return SZ_ERROR_ARCHIVE;
        filter->propsSize = (UInt32)size;
        memcpy(filter->props, header + pos, (size_t)size);
        pos += (unsigned)size;

        #ifdef XZ_DUMP
        printf("\nf[%d] = %2X: ", i, filter->id);
        {
            int i;
            for (i = 0; i < size; i++)
                printf(" %2X", filter->props[i]);
        }
        #endif
    }

    while (pos < headerSize)
        if (header[pos++] != 0)
            return SZ_ERROR_ARCHIVE;
    return SZ_OK;
}

SRes XzDec_Init(CMixCoder* p, const CXzBlock* block)
{
    int i;
    Bool needReInit = True;
    int numFilters = XzBlock_GetNumFilters(block);
    if (numFilters == p->numCoders)
    {
        for (i = 0; i < numFilters; i++)
            if (p->ids[i] != block->filters[numFilters - 1 - i].id)
                break;
        needReInit = (i != numFilters);
    }
    if (needReInit)
    {
        MixCoder_Free(p);
        p->numCoders = numFilters;
        for (i = 0; i < numFilters; i++)
        {
            const CXzFilter* f = &block->filters[numFilters - 1 - i];
            RINOK(MixCoder_SetFromMethod(p, i, f->id));
        }
    }
    for (i = 0; i < numFilters; i++)
    {
        const CXzFilter* f = &block->filters[numFilters - 1 - i];
        IStateCoder* sc = &p->coders[i];
        RINOK(sc->SetProps(sc->p, f->props, f->propsSize, p->alloc));
    }
    MixCoder_Init(p);
    return SZ_OK;
}

SRes XzUnpacker_Create(CXzUnpacker* p, ISzAlloc* alloc)
{
    MixCoder_Construct(&p->decoder, alloc);
    p->state = XZ_STATE_STREAM_HEADER;
    p->pos = 0;
    p->numStreams = 0;
    return SZ_OK;
}

void XzUnpacker_Free(CXzUnpacker* p)
{
    MixCoder_Free(&p->decoder);
}

SRes XzUnpacker_Code(CXzUnpacker* p, Byte* dest, SizeT* destLen,
                     const Byte* src, SizeT* srcLen, int finishMode, ECoderStatus* status)
{
    SizeT destLenOrig = *destLen;
    SizeT srcLenOrig = *srcLen;
    *destLen = 0;
    *srcLen = 0;
    *status = CODER_STATUS_NOT_SPECIFIED;
    for (;;)
    {
        SizeT srcRem = srcLenOrig - *srcLen;

        if (p->state == XZ_STATE_BLOCK)
        {
            SizeT destLen2 = destLenOrig - *destLen;
            SizeT srcLen2 = srcLenOrig - *srcLen;
            SRes res;
            if (srcLen2 == 0 && destLen2 == 0)
            {
                *status = CODER_STATUS_NOT_FINISHED;
                return SZ_OK;
            }

            res = MixCoder_Code(&p->decoder, dest, &destLen2, src, &srcLen2, False, finishMode, status);
            XzCheck_Update(&p->check, dest, destLen2);

            (*srcLen) += srcLen2;
            src += srcLen2;
            p->packSize += srcLen2;

            (*destLen) += destLen2;
            dest += destLen2;
            p->unpackSize += destLen2;

            RINOK(res);

            if (*status == CODER_STATUS_FINISHED_WITH_MARK)
            {
                Byte temp[32];
                unsigned num = Xz_WriteVarInt(temp, p->packSize + p->blockHeaderSize + XzFlags_GetCheckSize(p->streamFlags));
                num += Xz_WriteVarInt(temp + num, p->unpackSize);
                Sha256_Update(&p->sha, temp, num);
                p->indexSize += num;
                p->numBlocks++;

                p->state = XZ_STATE_BLOCK_FOOTER;
                p->pos = 0;
                p->alignPos = 0;
            }
            else if (srcLen2 == 0 && destLen2 == 0)
                return SZ_OK;

            continue;
        }

        if (srcRem == 0)
        {
            *status = CODER_STATUS_NEEDS_MORE_INPUT;
            return SZ_OK;
        }

        switch (p->state)
        {
            case XZ_STATE_STREAM_HEADER:
            {
                if (p->pos < XZ_STREAM_HEADER_SIZE)
                {
                    if (p->pos < XZ_SIG_SIZE && *src != XZ_SIG[p->pos])
                        return SZ_ERROR_NO_ARCHIVE;
                    p->buf[p->pos++] = *src++;
                    (*srcLen)++;
                }
                else
                {
                    RINOK(Xz_ParseHeader(&p->streamFlags, p->buf));
                    p->state = XZ_STATE_BLOCK_HEADER;
                    Sha256_Init(&p->sha);
                    p->indexSize = 0;
                    p->numBlocks = 0;
                    p->pos = 0;
                }
                break;
            }

            case XZ_STATE_BLOCK_HEADER:
            {
                if (p->pos == 0)
                {
                    p->buf[p->pos++] = *src++;
                    (*srcLen)++;
                    if (p->buf[0] == 0)
                    {
                        p->indexPreSize = 1 + Xz_WriteVarInt(p->buf + 1, p->numBlocks);
                        p->indexPos = p->indexPreSize;
                        p->indexSize += p->indexPreSize;
                        Sha256_Final(&p->sha, p->shaDigest);
                        Sha256_Init(&p->sha);
                        p->crc = CrcUpdate(CRC_INIT_VAL, p->buf, p->indexPreSize);
                        p->state = XZ_STATE_STREAM_INDEX;
                    }
                    p->blockHeaderSize = ((UInt32)p->buf[0] << 2) + 4;
                }
                else if (p->pos != p->blockHeaderSize)
                {
                    UInt32 cur = p->blockHeaderSize - p->pos;
                    if (cur > srcRem)
                        cur = (UInt32)srcRem;
                    memcpy(p->buf + p->pos, src, cur);
                    p->pos += cur;
                    (*srcLen) += cur;
                    src += cur;
                }
                else
                {
                    RINOK(XzBlock_Parse(&p->block, p->buf));
                    p->state = XZ_STATE_BLOCK;
                    p->packSize = 0;
                    p->unpackSize = 0;
                    XzCheck_Init(&p->check, XzFlags_GetCheckType(p->streamFlags));
                    RINOK(XzDec_Init(&p->decoder, &p->block));
                }
                break;
            }

            case XZ_STATE_BLOCK_FOOTER:
            {
                if (((p->packSize + p->alignPos) & 3) != 0)
                {
                    (*srcLen)++;
                    p->alignPos++;
                    if (*src++ != 0)
                        return SZ_ERROR_CRC;
                }
                else
                {
                    UInt32 checkSize = XzFlags_GetCheckSize(p->streamFlags);
                    UInt32 cur = checkSize - p->pos;
                    if (cur != 0)
                    {
                        if (cur > srcRem)
                            cur = (UInt32)srcRem;
                        memcpy(p->buf + p->pos, src, cur);
                        p->pos += cur;
                        (*srcLen) += cur;
                        src += cur;
                    }
                    else
                    {
                        Byte digest[XZ_CHECK_SIZE_MAX];
                        p->state = XZ_STATE_BLOCK_HEADER;
                        p->pos = 0;
                        if (XzCheck_Final(&p->check, digest) && memcmp(digest, p->buf, checkSize) != 0)
                            return SZ_ERROR_CRC;
                    }
                }
                break;
            }

            case XZ_STATE_STREAM_INDEX:
            {
                if (p->pos < p->indexPreSize)
                {
                    (*srcLen)++;
                    if (*src++ != p->buf[p->pos++])
                        return SZ_ERROR_CRC;
                }
                else
                {
                    if (p->indexPos < p->indexSize)
                    {
                        UInt64 cur = p->indexSize - p->indexPos;
                        if (srcRem > cur)
                            srcRem = (SizeT)cur;
                        p->crc = CrcUpdate(p->crc, src, srcRem);
                        Sha256_Update(&p->sha, src, srcRem);
                        (*srcLen) += srcRem;
                        src += srcRem;
                        p->indexPos += srcRem;
                    }
                    else if ((p->indexPos & 3) != 0)
                    {
                        Byte b = *src++;
                        p->crc = CRC_UPDATE_BYTE(p->crc, b);
                        (*srcLen)++;
                        p->indexPos++;
                        p->indexSize++;
                        if (b != 0)
                            return SZ_ERROR_CRC;
                    }
                    else
                    {
                        Byte digest[SHA256_DIGEST_SIZE];
                        p->state = XZ_STATE_STREAM_INDEX_CRC;
                        p->indexSize += 4;
                        p->pos = 0;
                        Sha256_Final(&p->sha, digest);
                        if (memcmp(digest, p->shaDigest, SHA256_DIGEST_SIZE) != 0)
                            return SZ_ERROR_CRC;
                    }
                }
                break;
            }

            case XZ_STATE_STREAM_INDEX_CRC:
            {
                if (p->pos < 4)
                {
                    (*srcLen)++;
                    p->buf[p->pos++] = *src++;
                }
                else
                {
                    p->state = XZ_STATE_STREAM_FOOTER;
                    p->pos = 0;
                    if (CRC_GET_DIGEST(p->crc) != GetUi32(p->buf))
                        return SZ_ERROR_CRC;
                }
                break;
            }

            case XZ_STATE_STREAM_FOOTER:
            {
                UInt32 cur = XZ_STREAM_FOOTER_SIZE - p->pos;
                if (cur > srcRem)
                    cur = (UInt32)srcRem;
                memcpy(p->buf + p->pos, src, cur);
                p->pos += cur;
                (*srcLen) += cur;
                src += cur;
                if (p->pos == XZ_STREAM_FOOTER_SIZE)
                {
                    p->state = XZ_STATE_STREAM_PADDING;
                    p->numStreams++;
                    p->padSize = 0;
                    if (!Xz_CheckFooter(p->streamFlags, p->indexSize, p->buf))
                        return SZ_ERROR_CRC;
                }
                break;
            }

            case XZ_STATE_STREAM_PADDING:
            {
                if (*src != 0)
                {
                    if (((UInt32)p->padSize & 3) != 0)
                        return SZ_ERROR_NO_ARCHIVE;
                    p->pos = 0;
                    p->state = XZ_STATE_STREAM_HEADER;
                }
                else
                {
                    (*srcLen)++;
                    src++;
                    p->padSize++;
                }
                break;
            }
        }
    }
    /*
       if (p->state == XZ_STATE_FINISHED)
     *status = CODER_STATUS_FINISHED_WITH_MARK;
       return SZ_OK;
     */
}

Bool XzUnpacker_IsStreamWasFinished(CXzUnpacker* p)
{
    return (p->state == XZ_STATE_STREAM_PADDING) && (((UInt32)p->padSize & 3) == 0);
}